#include <stdio.h>
#include <stdlib.h>
#include "doubleL.h"
#include "io_queue.h"
#include "sim.h"

/* global */
unsigned clock;
int debug;

list* queue; /* waiting process queue */
list* done_queue; /* completed process queue */

unsigned io_complete_time;

unsigned count = 0;

static void ioNext(unsigned delta);
static void printIOJob(PCB* process, char* operation);

void initIOQueue()
{
	queue = list_init();
	done_queue = list_init();
}

void incrementIOQueue()
{
	unsigned previous_io_time, next_io_time, delta = 0;
	PCB* tmp;


	if(!list_empty(queue)){

		if( clock >= io_complete_time){
			delta = clock - io_complete_time;
			
			tmp = (PCB*) list_get_element(queue, 1);
			list_remove(queue, 1);
			setCompletedIO(tmp, getCompletedIO(tmp)+1);

			/* check to see if there is a following
			 * io read, if so add to end of queue
			 * or schedule */
			if(getCompletedIO(tmp) < getTimeLineSize(tmp))
			{
				previous_io_time = getTimeLineValue(tmp, getCompletedIO(tmp) - 1);
				next_io_time = getTimeLineValue(tmp, getCompletedIO(tmp));
				if(next_io_time == previous_io_time){
					/* append back to end of queue */
					list_append(queue, (ElemType)tmp);
				} else {
					list_append(done_queue, (ElemType) tmp);
					count--;
				}
			} else {
				list_append(done_queue,(ElemType)tmp);
				count--;
			}
			printIOJob(tmp, "Complete");
			ioNext(delta);
		}
	}
}

void doIO(PCB* proc)
{
	if(list_empty(queue)){
		io_complete_time = clock + IO_OP_TIME_MS;
		printIOJob(proc, "Start   ");
	} else {
		printIOJob(proc, "Queued");
	}
	list_append(queue, (ElemType)proc);
	count++;
}

unsigned nextIODoneTime()
{
	if(list_empty(queue)) {
		return 0;
	} else {
		return io_complete_time; 
	}
}

int noCompletedProcs(){
	return list_empty(done_queue);
}

PCB* getIOCompletedProc()
{
	PCB* ret_val = (PCB*) list_get_element(done_queue, 1);
	list_remove(done_queue, 1);
	return ret_val;
}

int getIOLength()
{
	return count;
}

static void ioNext(unsigned delta)
{
	/* run next io job */
	if(!list_empty(queue)){
		io_complete_time = ( clock + IO_OP_TIME_MS ) - delta;
		printIOJob((PCB*) list_get_element(queue, 1), "Start   ");
	}
}

static void printIOJob(PCB* process, char* operation)
{
	if(debug) printf("\tIO:%s:%s PID:%d CPUTime:%u/%u IOOP:%u/%u CLK:%u\n",
            operation,
			getName(process), getPid(process),
			getCompletedTime(process), getCpuTime(process),
			getCompletedIO(process), getTimeLineSize(process),
			clock);
}



